package org.simpleframework.core;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.simpleframework.aop.annotation.Aspect;
import org.simpleframework.core.annotation.Component;
import org.simpleframework.core.annotation.Controller;
import org.simpleframework.core.annotation.Repository;
import org.simpleframework.core.annotation.Service;
import org.simpleframework.util.ClassUtil;
import org.simpleframework.util.ValidationUtil;

import java.lang.annotation.Annotation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanContainer {
    /**
     * 存放所有被配置标记的对象的Map
     */
    private final Map<Class<?>,Object> beanMap=new ConcurrentHashMap();

    /**
     * 加载bean的注解列表
     */
    private static final List<Class<? extends Annotation>> BEAN_ANNOTATION
            = Arrays.asList(Component.class, Controller.class, Service.class, Repository.class, Aspect.class);

    /**
     * 获取Bean容器实例
     * @return BeanContainer
     */
    public static BeanContainer getInstance(){
        return ContainerHolder.HOLDER.instance;
    }

    private enum ContainerHolder{
        HOLDER;
        private BeanContainer instance;
        ContainerHolder(){
           instance= new BeanContainer();
        }
    }

    /**
     * 容器是否被加载过
     */
    private  boolean loaded=false;

    /**
     *是否加载过Bean
     * @return 是否已经加载
     */
    public boolean isLoaded(){
        return loaded;
    }
    /**
     * Bean实例数量
     * @return 数量
     */
    public  int size(){
        return  beanMap.size();
    }

    /**
     * 扫描加载所有的Bean
     * @param packageName 包名
     */
    public synchronized void loadBeans(String packageName){
        //判断容器是否被加载过
        if(isLoaded()){
            log.warn("BeanContainer has been loaded.");
            return;
        }
        Set<Class<?>> classSet= ClassUtil.extractPackageClass(packageName);
        if (ValidationUtil.isEmpty(classSet)) {
            log.warn("extract nothing from packageName"+ packageName);
            return;
        }
        for (Class<?> clazz : classSet) {
            for (Class<? extends Annotation> annotation : BEAN_ANNOTATION) {
                //如果类上标记了自定义的注解
                if(clazz.isAnnotationPresent(annotation)){
                    //将目标类本身作为键，目标实例作为值传入beanMap中
                    beanMap.put(clazz,ClassUtil.newInstance(clazz,true));
                }
            }
        }
        loaded=true;
    }

    /**
     * 添加一个class对象以及其Bean实例
     * @param clazz Class对象
     * @param bean  Bean实例
     * @return    原有的Bean实例返回，没有返回Null
     */
   public Object addBean(Class<?> clazz,Object bean){
       return beanMap.put(clazz,bean);
    }

    /**
     *移除一个IOC容器管理的对象
     * @param clazz
     * @return 删除Bean实例，没有返回Null
     */
   public  Object removeBean(Class<?> clazz){
        return beanMap.remove(clazz);
   }

    /**
     * 获取Bean实例
     * @param clazz
     * @return
     */
   public  Object getBean(Class<?> clazz){
       return beanMap.get(clazz);
   }

    /**
     *  获取容器内所有Class
     * @return Class集合
     */
   public  Set<Class<?>> getClasses(){
       return beanMap.keySet();
   }

    /**
     * 获取所有Bean集合
     * @return  Bean集合
     */
   public Set<Object> getBeans(){
       return new HashSet<Object>(beanMap.values());
   }

    /**
     * 根据注解筛选出Bean的Class集合
     * @param annotation 注解
     * @return Class集合
     */
   public Set<Class<?>> getClassByAnnotation(Class<? extends Annotation> annotation){
        //1.获取beanMap的所有class对象
       Set<Class<?>> keySet=getClasses();
       if(ValidationUtil.isEmpty(keySet)){
           log.warn("nothing in beanMap");
           return null;
       }
       //2.通过注解筛选被标记过的Class对象，并添加到classSet中
       Set<Class<?>> classSet=new HashSet<>();
       for (Class<?> clazz : keySet) {
           //类是否被相关标记
           if (clazz.isAnnotationPresent(annotation)) {
               classSet.add(clazz);
           }
       }
       return classSet.size()>0?classSet:null;
   }

    /**
     * 通过接口或父类获取类或者子类的Class集合，不包括类本身
     * @param interfaceOrClass 接口或者父类Class
     * @return  Class集合
     */
    public Set<Class<?>> getClassBySuper(Class<? > interfaceOrClass){
        //1.获取beanMap的所有class对象
        Set<Class<?>> keySet=getClasses();
        if(ValidationUtil.isEmpty(keySet)){
            log.warn("nothing in beanMap");
            return null;
        }
        //2.判断keySet里的元素是否是传入接口或类的子类，如果是，添加到classSet中
        Set<Class<?>> classSet=new HashSet<>();
        for (Class<?> clazz : keySet) {
            //判断keySet里的元素是否是子类
            if (interfaceOrClass.isAssignableFrom(clazz)&&!clazz.equals(interfaceOrClass)) {
                classSet.add(clazz);
            }
        }
        return classSet.size()>0?classSet:null;
    }



}
